from dataclasses import dataclass, asdict
from typing import List, Optional, Tuple, Union
from weakref import WeakValueDictionary

from fastapi import Depends
from sqlalchemy import ColumnElement, and_, or_
from sqlalchemy.orm import Query, Session


from app.configs.config import QUERY_ALL_PAGE_SIZE, QUERY_DEFAULT_PAGE_SIZE
from app.models import BaseModel
from app.initialization.init_sqlalchemy import entity_db_session

from app.models import TestBaseModel

_entities_models = WeakValueDictionary()


@dataclass
class RangeFilter:
    """表示一个范围筛选条件的类。
    属性：
        column (str): 要筛选的列名。
        start (Union[str, int, float]): 范围的起始值，可以是字符串、整数或浮点数。
        end (Union[str, int, float]): 范围的结束值，可以是字符串、整数或浮点数。
        start_flag (str): 范围起始值的比较符号，默认为 ">="。
        end_flag (str): 范围结束值的比较符号，默认为 "<="。

    示例：
        >>> range_filter = RangeFilter(column='age', start=18, end=30)
        >>> print(range_filter)
        RangeFilter(column='age', start=18, end=30, start_flag='>=', end_flag='<=')
    """
    column: str
    start: Union[str, int, float] = None
    end: Union[str, int, float] = None
    start_flag: str = ">="
    end_flag: str = "<="

    def _to_sqlalchemy_open_open(self) -> ColumnElement[bool]:
        """左开右开 (a, b)
        """

        conditions = []

        if self.start is not None:
            conditions.append(
                getattr(self.column, self.start_flag) > (self.start))

        if self.end is not None:
            conditions.append(getattr(self.column, self.end_flag) < (self.end))

        return and_(*conditions)

    def _to_sqlalchemy_open_close(self) -> ColumnElement[bool]:
        """左开右闭 (a, b]
        """

        conditions = []
        if self.start is not None:
            conditions.append(
                getattr(self.column, self.start_flag) > (self.start))
        if self.end is not None:
            conditions.append(
                getattr(self.column, self.end_flag) <= (self.end))
        return and_(*conditions)

    def _to_sqlalchemy_close_open(self) -> ColumnElement[bool]:
        """左闭右开 [a, b)
        """
        conditions = []
        if self.start is not None:
            conditions.append(
                getattr(self.column, self.start_flag) >= (self.start))
        if self.end is not None:
            conditions.append(getattr(self.column, self.end_flag) < (self.end))
        return and_(*conditions)

    def _to_sqlalchemy_close_close(self) -> ColumnElement[bool]:
        """左闭右闭 [a, b]
        """
        conditions = []
        if self.start is not None:
            conditions.append(
                getattr(self.column, self.start_flag) >= (self.start))
        if self.end is not None:
            conditions.append(
                getattr(self.column, self.end_flag) <= (self.end))

        return and_(*conditions)

    def to_sqlalchemy(self) -> Union[ColumnElement[bool], None]:
        """将范围筛选条件转换为 SQLAlchemy 可识别的表达式。

        该方法根据 `start_flag` 和 `end_flag` 属性组合，确定范围的类型，
        并调用相应的私有方法将范围转换为 SQLAlchemy 表达式。

        支持的范围类型：
            - "><": 左开右开
            - "><=": 左开右闭
            - ">=<": 左闭右开
            - ">=<=": 左闭右闭

        参数：
            无

        返回值：
            ColumnElement[bool]: 如果范围筛选条件有效，则返回 SQLAlchemy 表达式，
                                否则返回 None。

        异常：
            ValueError: 如果 `start_flag` 和 `end_flag` 组合不支持，则抛出该异常。

        示例：
            >>> range_filter = RangeFilter(column='age', start=18, end=30, start_flag='>', end_flag='<=')
            >>> range_filter.dispose_flag()
            ColumnElement(age > 18 AND age <= 30)
        """

        _func_dict = {
            "><": self._to_sqlalchemy_open_open,
            "><=": self._to_sqlalchemy_open_close,
            ">=<": self._to_sqlalchemy_close_open,
            ">=<=": self._to_sqlalchemy_close_close,
        }

        if self.start is None and self.end is None:
            return None

        func_ = _func_dict.get(self.start_flag + self.end_flag, None)
        if func_ is None:
            raise ValueError(f"不支持的区间类型: {self.start_flag + self.end_flag}")

        return func_()


class ModelMetaClass(type):

    def __new__(mcs, name, bases, attrs):
        if '_model' not in attrs:
            raise AttributeError(
                "class {} should set these class attributes: _model".format(name))

        filters = attrs.get("_filter_fields") or []
        ranges = attrs.get("_range_filter_fields") or []
        likes = attrs.get("_like_filter_fields") or []

        children = attrs.get("_children")
        if children:
            _children = list()
            for child in children:
                if len(child.split(".")) != 2:
                    raise AttributeError(
                        "child should be set like ChildrenEntiy.parent_id")
                _children.append(child.split("."))
            attrs['_children'] = _children

        cls = type.__new__(mcs, name, bases, attrs)
        cls._entities_models = _entities_models

        if attrs.get("_model"):
            entity = attrs.get("_model")
            does_not_exists = (set(filters) | set(ranges) | set(
                likes)) - set(entity.__table__.columns.keys())
            if does_not_exists:
                raise AttributeError(
                    f"class {entity.__name__} does not have attrs {does_not_exists}")

            cls._entities_models[entity.__name__] = cls

        return cls


class BaseEntity(metaclass=ModelMetaClass):

    _model: BaseModel.__class__ = None

    # 精确匹配，
    _exact_filter_fields: List = None

    # like，%是一个特殊符号，
    # 如果查询为 XXX，将查询 %XXX%
    # 如果查询为 %XXX，将直接使用 %XXX; 同理，XXX% %XXX%
    # 此操作旨在一定程度上保留 XXX% 查询的能力，
    # 在有索引的情况下，XXX% 效率高于 %XXX%
    _like_filter_fields: List = None

    # 范围匹配 (包含范围的端点)，默认 [], list tuple
    # 加入特殊处理，[] / () / [) / (], dict
    # 例如：{start:{key:[ value: xxxx}, end:{key:[ value: xxxx}}
    _range_filter_fields: List = None

    # # 精确匹配 外键
    # _foreign_filter_fields: List = None

    # 排序, ["create_time", "-update_time"]
    _order_by_fields: List = None    # 排序

    # children
    _children: List[str] = None

    # _pagination = PageNumberPagination()
    # _page_info = None

    # _filter_list = []

    @classmethod
    def get_query(cls, is_available: bool = True) -> Query:

        q: Query = cls._model.query

        if is_available is not None:
            q = q.filter_by(is_available=is_available)

        # if USE_SQLALCHEMY_BINDS:
        #     return q.with_session(cls.new_session())
        return q

    @classmethod
    def get_all(cls,
                *,
                order_by: str = "-create_time",
                limit: int = QUERY_ALL_PAGE_SIZE,
                is_available: Union[bool, None] = True,
                **kwargs) -> List["BaseEntity._model"]:
        """
        根据id获取entity对象，该方法设置了默认最大分页，
        请修改配置中的MAX_PAGE_SIZE来修改默认最大分页

        :param is_available: 可用查询，默认为 True。可用:True,不可用：Fasle，All：None
        :type active_only: bool, optional

          查询的entity对象列表
        """
        entity_list, _ = cls.get_by_filter(
            order_by=order_by,
            limit=limit,
            is_available=is_available,
            **kwargs)
        return entity_list

    @classmethod
    def get_by_filter(cls,
                      *,
                      order_by: str = "-create_time",
                      offset: int = 0,
                      limit: int = QUERY_DEFAULT_PAGE_SIZE,
                      require_count: bool = False,
                      is_available: Union[bool, None] = True,
                      return_query: bool = False,
                      **kwargs) -> Union[Tuple[Query, None], Tuple[List["BaseEntity._model"], int]]:
        """根据条件过滤entity列表，该方法返回一个两个元素的元组
            第一个值为count，代表经过过滤之后总共获取的entity个数
            第二个值为list，为经过排序和切片取值过后的entity列表

        :param order_by: 排序字段, defaults to "-create_time", create_time 倒序
        :type order_by: str, optional
        :param offset: offset, defaults to 0
        :type offset: int, optional
        :param limit: limit, defaults to 10
        :type limit: int, optional
        :param require_count: 是否返回经过条件过滤之后的合计值，
                              这个方法将单独进行一次sql中的select
                              操作，会影响效率。默认 False 。
        :type require_count: bool, optional
        :param is_available: 可用查询，默认为 True。可用:True,不可用：Fasle，All：None
        :type is_available: bool, optional
        :param return_query: 返回 Query 对象，默认为 False。True：返回 Query 对象, 方便 all first 等操作
        :type return_query: bool, optional
        :param **kwargs:
            过滤条件以关键字的形式传入，关键字过滤的方式在以下几个类属性中设置。
                _filter_fields: List：            使用 == 过滤,   and 相连；
                _range_filter_fields: List        使用<= >= 过滤，and 相连，要求参数一定是一个包含两个要素（start,end)的序列；
                _contains_like_filter_fields: List         使用like过滤，  or相连，实参会自动被加上左右模糊；

        :return:  如果 return_query 为 True，则返回 (Query 对象, None)。
                  否则，返回 (过滤后的数据列表,  如果 require_count 为 True 则返回数据总数，否则返回 None)。
        :rtype: Union[Tuple[Query, None], Tuple[List["BaseModel._model"], Optional[int]]]
        """

        query: Query = cls._get_full_query(
            order_by,
            is_available,
            **kwargs,
        )
        q: Query = query.limit(limit).offset(offset)
        if return_query:
            return q, None

        if not require_count:
            return q.all(), None

        count = query.count() if require_count else 0
        return q.all(), count

    @classmethod
    def _get_full_query(cls,
                        order_by: str = "-create_time",
                        active_only: bool = True,
                        **kwargs):
        """
        获取完整的query，1. 获取 filter 2. 获取 filtered_query 3. 添加order_by """
        query = cls._get_filtered_query(active_only, **kwargs)

        order_by_list = [element_order_by.strip()
                         for element_order_by in order_by.split(",")]

        if cls._order_by_filter_fields:
            order_by_list = cls._order_by_filter_fields

        order_list = []
        for element_order_by in order_by_list:
            is_asc = True
            if element_order_by.startswith("-"):
                is_asc = False
                element_order_by = element_order_by[1:]

            order_column = getattr(cls._model, element_order_by)
            order_stmt = order_column.asc() if is_asc else order_column.desc()

            order_list.append(order_stmt)

        query = query.order_by(*order_list)
        return query

    @classmethod
    def _get_filtered_query(cls, is_available, **kwargs):
        """
            获取经过过滤的BaseQuery对象
        """

        q = cls.get_query(is_available)

        # handle filter_keys
        f = cls._get_filter(**kwargs)
        return q.filter(f)

    @classmethod
    def _get_filter(cls, *,
                    only_exact_filter: bool = False,
                    only_like_filter: bool = False,
                    only_range_filter: bool = False,
                    # only_foreign_filter: bool = False,
                    filter_inside_and: bool = True,
                    filter_interval_and: bool = True,
                    **kwargs):
        """根据Model允许的过滤条件获取filter对象
        only_XXX 只处理 XXX 对应的 filter
        filter_inside_and


        only_XXX 只处理 XXX 对应的 filter, 
        例如：_exact_filter=A _like_filter=A，
                如果不存在 only_XXX 两个过滤会同时生效

        :param only_exact_filter: 只使用 _exact_filter，默认False
        :type only_exact_filter: bool, optional
        :param only_like_filter: 只使用 _like_filter，默认False
        :type only_like_filter: bool, optional
        :param only_range_filter: 只使用 _range_filter，默认False
        :type only_range_filter: bool, optional
        :param only_range_filter: 只使用 _range_filter，默认False
        :type only_range_filter: bool, optional

        """
        _exact_filter_list = list()
        _range_filter_list = list()
        _like_filter_list = list()

        # handle _exact_filter_fields
        for key in cls._exact_filter_fields or []:

            if key in kwargs:
                value: Union[list, tuple, set, str, None] = kwargs[key]

                if isinstance(value, (list, tuple, set)):
                    _exact_filter_list.append(
                        getattr(cls._model, key).in_(value))

                elif isinstance(value, str):
                    _exact_filter_list.append(
                        getattr(cls._model, key) == value)

                elif value is None:
                    _exact_filter_list.append(
                        getattr(cls._model, key).is_(None))

                else:
                    raise ValueError(
                        f"""{key}:{value}, unsupported value type "{type(value)}" """)

        # handle _range_filter_fields
        for key in cls._range_filter_fields or []:

            # 加入特殊处理
            value = kwargs.get(key) or (None, None)

            # time = [start, end] or time = (start, end) or time = {start, end}
            if isinstance(value, (list, tuple, set)) and len(value) == 2:
                like_filter = RangeFilter(key, *value).to_sqlalchemy()

            # time = RangeFilter(column, start, end, start_flag, end_flag)
            elif isinstance(value, RangeFilter):
                like_filter = value.to_sqlalchemy()

            else:
                raise ValueError(
                    f"""{key}:{value}, unsupported value type "{type(value)}" """)

            _range_filter_list.append(like_filter)

        # handle _like_filter_fields
        for key in cls._like_filter_fields or []:

            try:
                value = kwargs[key]
                if value is None:
                    _like_filter_list.append(
                        getattr(cls._model, key).is_(None))
                else:
                    if value.count("%") == 0:
                        _like_filter_list.append(
                            getattr(cls._model, key).like(f"%{value}%"))
                    else:
                        _like_filter_list.append(
                            getattr(cls._model, key).like(value))
            except KeyError:
                continue

        return cls._apply_filters(
            exact_filter=_exact_filter_list,
            like_filter=_like_filter_list,
            range_filter=_range_filter_list,
            only_exact=only_exact_filter,
            only_like=only_like_filter,
            only_range=only_range_filter,
            inside_and=filter_inside_and,
            interval_and=filter_interval_and,
        )

    @staticmethod
    def _apply_filters(exact_filter: list, like_filter: list, range_filter: list,
                       only_exact: bool = False, only_like: bool = False, only_range: bool = False,
                       inside_and: bool = True, interval_and: bool = True):
        """
            应用过滤条件
        """

        if only_exact:
            if inside_and:
                return and_(*exact_filter)
            else:
                return or_(*exact_filter)

        if only_like:
            if inside_and:
                return and_(*like_filter)
            else:
                return or_(*like_filter)

        if only_range:
            if inside_and:
                return and_(*range_filter)
            else:
                return or_(*range_filter)

        if inside_and:
            inside = [and_(*sublist)
                      for sublist in [exact_filter, like_filter, range_filter]
                      if len(sublist) > 0]
        else:
            inside = [or_(*sublist)
                      for sublist in [exact_filter, like_filter, range_filter]
                      if len(sublist) > 0]

        if interval_and:
            return and_(*inside)
        else:
            return or_(*inside)

    @classmethod
    def create(cls, db_session: Session, entity: Union[dict, None] = None) -> BaseModel:
        """创建一个Entity
            这个方法应该在不同的Model中被重写，应该有更加详细的函数参数。

        :param **entity 需要传入的字段
        """

        if entity is None:
            entity = cls._model()
        else:

            entity = cls._model(**entity)
            
        db_session.add(entity)
        db_session.flush()
        return entity


class TestBaseEntity(BaseEntity):

    _model = TestBaseModel
    _exact_filter_fields = ["id"]
    _like_filter_fields = None
    _range_filter_fields = ["create_time"]
